#include <serialportthread.h>
#include <QVector>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QMessageBox>
#include <QSettings>
#include <debuglog.h>

SerialPortThread::SerialPortThread(QObject *parent) : QThread(parent)
{
    debugLog = new DebugLog();
    serialPort = new QSerialPort(this);
    threadFlag = true;
    openSerialPort();
    serialPort->moveToThread(this);
}

void SerialPortThread::sendData(quint8 cmd, QVector<quint8> cmdData)
{
    QByteArray data = encapData(cmd, cmdData);
    writeData(data);
}

void SerialPortThread::run()
{
    debugLog->writelog("Thread Run");
    connect(serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(handleError(QSerialPort::SerialPortError)));
    connect(serialPort, SIGNAL(readyRead()), this, SLOT(readData()));
}

void SerialPortThread::stop()
{
    threadFlag = false;
}

bool SerialPortThread::openSerialPort()
{
    bool flag = false;

    QSettings settingsIni("/home/EPOS.ini", QSettings::IniFormat);
    settingsIni.beginGroup("serialport");

    //serial->setPortName(settingsIni.value("PortName").toString());
    serialPort->setPortName("ttyO3");
    //serial->setBaudRate(settingsIni.value("BaudRate").toInt());
    serialPort->setBaudRate(QSerialPort::Baud115200);
    //serial->setDataBits((QSerialPort::DataBits)settingsIni.value("DataBits").toInt());
    serialPort->setDataBits(QSerialPort::Data8);
    //serial->setParity((QSerialPort::Parity)(settingsIni.value("Parity").toInt()));
    serialPort->setParity(QSerialPort::NoParity);
    //serial->setStopBits((QSerialPort::StopBits)settingsIni.value("StopBits").toInt());
    serialPort->setStopBits(QSerialPort::OneStop);
    //serial->setFlowControl((QSerialPort::FlowControl)settingsIni.value("FlowControl").toInt());
    serialPort->setFlowControl(QSerialPort::NoFlowControl);

    settingsIni.endGroup();
    if (serialPort->open(QIODevice::ReadWrite)) {
        flag = true;
        debugLog->writelog("Open Serial Success");
    } else {
        debugLog->writelog("Open Serial Error");
    }

    return flag;
}

void SerialPortThread::closeSerialPort()
{
    if (serialPort->isOpen())
        serialPort->close();
}

void SerialPortThread::writeData(const QByteArray &data)
{
    if (serialPort->isOpen())
        serialPort->write(data);
}

void SerialPortThread::handleError(QSerialPort::SerialPortError error)
{
    if (error == QSerialPort::ResourceError) {
        debugLog->writelog("Serial Error:" + serialPort->errorString());
        closeSerialPort();
    }
}

void SerialPortThread::readData()
{
    QByteArray data = serialPort->readAll();
    if(!data.isNull() && data.length() > 0)
    {
        debugLog->writelog("Start");

        for(int i = 0; i < data.length(); i++)
        {
            quint8 num = static_cast<quint8>(data.at(i));
            // check header
            if(isHeader(num) == 1)
            {
                debugLog->writelog("Header");

                // check data
                if(isValid() == 1)
                {
                    debugLog->writelog("Package");

                    // analysis data
                    int deviceId = ComData.at(0) + 256 & ComData.at(1);
                    quint8 serialNum = ComData.at(2);
                    quint8 funcNum = ComData.at(3);
                    quint8 cmdNum = ComData.at(4);

                    // 指令类型
                    quint8 cmd = 0x00;
                    // 指令内容长度
                    int dataLen = 0;

                    if(cmdNum == 0x20)
                    {
                        cmd = 0x20;
                        dataLen = 2;
                    }
                    else if(cmdNum == 0x21){
                        cmd = 0x21;
                        dataLen = 12;
                    }
                    else if(cmdNum == 0x22){
                        cmd = 0x22;
                        dataLen = 4;
                    }
                    else if(cmdNum == 0x23){
                        cmd = 0x23;
                        dataLen = 2;
                    }

                    if(cmd != 0x00 && dataLen > 0)
                    {
                        quint8 ack[dataLen];
                        for(int i = 0; i < dataLen; i++)
                        {
                            ack[i] = ComData.at(8 + i);
                        }

                        emit SerialPortSignal(cmd, ack, dataLen);
                    }

                    initialSerialData();
                }
                else
                {
                    debugLog->writelog("Unvaliable Package");
                }
            }
        }
    }
}

// 数据头检测
quint8 SerialPortThread::isHeader(quint8 num)
{
    if(HeaderFlag == 0 && ReadIndex == 0 && num == rHeader[0])
    {
        ReadIndex++;
    }
    else if(HeaderFlag == 0 && ReadIndex == 1 && num == rHeader[1])
    {
        ReadIndex++;
    }
    else if(HeaderFlag == 0 && ReadIndex == 2 && num == rHeader[2])
    {
        ReadIndex++;
    }
    else if(HeaderFlag == 0 && ReadIndex == 3 && num == rHeader[3])
    {
        ReadIndex++;
    }
    else if(HeaderFlag == 0 && ReadIndex == 4 && num == rHeader[4])
    {
        HeaderFlag = 1;
        ReadIndex++;
    }
    else if(HeaderFlag == 1 && ReadIndex > 4){

        if(DataLen > 0 && ((DataLen + 12) == ReadIndex)){
            ComData.append(num);
            return 1;
        }
        if(ReadIndex == 11 && ComData.count() == 6){
            DataLen = 256 * num + ComData.at(5);
        }
        ReadIndex++;
        ComData.append(num);
    }
    else{
        initialSerialData();
    }
    return 0;
}

quint8 SerialPortThread::isValid()
{
    quint8 sum_Header = 0;
    if(ComData.count() == 8 + DataLen)
    {
        for(int i = 0; i < 7; i++)
        {
            sum_Header = sum_Header + ComData.at(i);
        }

        if(sum_Header == ComData.at(7))
        {
            quint8 crcArray[DataLen - 2];
            for(int j = 0; j < DataLen - 2; j++)
            {
                crcArray[j] = ComData.at(j + 8);
            }

            quint8 crc2[2];
            if(CRC16(crcArray, DataLen - 2, crc2) == 1)
            {
                if((crc2[0] == ComData.at(ComData.count() - 2)) && (crc2[1] == ComData.at(ComData.count() - 1)))
                {
                    return 1;
                }
                else
                {
                    debugLog->writelog("CRC16 Error");
                }
            }

        }
        else
        {
            debugLog->writelog("Header Sum Error");
        }
    }

    initialSerialData();
    return 0;
}

void SerialPortThread::initialSerialData()
{
    HeaderFlag = 0;
    ReadIndex = 0;
    DataLen = 0;
    ComData.clear();
}

quint8 SerialPortThread::CRC16(quint8* puchMsg,quint16 DataLen,quint8 CRC16[2])
{
    quint8 uchCRCHi = 0xFF;
    quint8 uchCRCLo = 0xFF;
    quint16 index = 0;
    if (puchMsg == NULL || CRC16 == NULL || DataLen < 1) return 0;
    while(DataLen--)
    {
        index = uchCRCHi ^ (*puchMsg++);
        uchCRCHi = uchCRCLo ^ auchCRCHigh[index];
        uchCRCLo = auchCRCLow[index];
    }

    CRC16[1] = uchCRCHi;
    CRC16[0] = uchCRCLo;
    return 1;
}

QByteArray SerialPortThread::encapData(quint8 cmd, QVector<quint8> cmdData)
{
    QVector<quint8> data;

    // 0xaa 0xaa 0xc0 0xc0 0xc0
    data.append(rHeader[0]);
    data.append(rHeader[1]);
    data.append(rHeader[2]);
    data.append(rHeader[3]);
    data.append(rHeader[4]);

    // device ID
    data.append(0x01);
    data.append(0x00);

    // serial ID
    data.append(0x01);

    // function ID
    data.append(0x01);

    // cmd
    data.append(cmd);

    // cmd+crc length
    int len = cmdData.count() + 2;
    quint8 highLen = len / 256;
    quint8 lowLen = len % 256;
    data.append(lowLen);
    data.append(highLen);

    // sum
    quint8 sum = 0;
    for(int i = 0; i < 7; i++)
    {
        sum = sum + data.at(data.count() - 7 + i);
    }
    data.append(sum);

    // cmd data
    quint8 crcArray[cmdData.count()];
    for(int j = 0; j < cmdData.count(); j++)
    {
        data.append(cmdData.at(j));
        crcArray[j] = ComData.at(j);
    }

    // crc16
    quint8 crc2[2];
    if(CRC16(crcArray, 2, crc2) == 1)
    {
        data.append(crc2[0]);
        data.append(crc2[1]);
    }

    QByteArray result;
    result.resize(data.count());
    for(int d = 0; d < data.count(); d++)
    {
        result[d] = data.at(d);
    }

    return result;
}
